package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.aggregations.NestedAggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.evil.application.enums.form.control.ControlCodeEnum;
import com.evil.application.enums.order.OrderSpecialControlEnum;
import com.evil.application.enums.reporting.ReportingTargetTypeEnum;
import com.evil.application.enums.reporting.SummaryTypeEnum;
import com.evil.application.pojo.dto.es.*;
import com.evil.application.pojo.dto.form.control.code.ControlDTO;
import com.evil.application.pojo.dto.form.control.code.NumberControlDTO;
import com.evil.application.pojo.dto.order.OrderPageReqDTO;
import com.evil.application.pojo.dto.order.es.SaveESOrderDTO;
import com.evil.application.pojo.dto.order.related.RelatedBaseReqDTO;
import com.evil.application.pojo.dto.order.related.RelatedDataListDTO;
import com.evil.application.pojo.dto.order.related.RelatedDataListReqDTO;
import com.evil.application.pojo.dto.order.related.RelatedOrderListReqDTO;
import com.evil.application.pojo.dto.reporting.*;
import com.evil.application.pojo.dto.reporting.data.RefreshAppOrderReqDTO;
import com.evil.application.pojo.dto.reporting.data.list.AccurateQueryReqDTO;
import com.evil.application.pojo.dto.reporting.data.list.ReportingDataListReqDTO;
import com.evil.application.pojo.entity.ApplicationFormControl;
import com.evil.application.pojo.entity.ApplicationInfo;
import com.evil.application.pojo.entity.ApplicationOrder;
import com.evil.application.pojo.entity.ApplicationOrderNodeHandler;
import com.evil.application.service.ApplicationFormControlService;
import com.evil.application.service.ElasticsearchService;
import com.evil.application.util.ApplicationUtil;
import com.evil.application.util.OrderDataUtil;
import com.evil.common.application.dto.judge.JudgeOr;
import com.evil.common.application.dto.order.OrderPageDataRespDTO;
import com.evil.common.application.dto.order.OrderQueryReqDTO;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.dto.order.data.ControlDataValueDTO;
import com.evil.common.application.enums.QueryTypeEnum;
import com.evil.common.application.enums.judge.JudgeSignEnum;
import com.evil.common.application.enums.order.OrderProcessStatusEnum;
import com.evil.common.application.enums.order.OrderSearchTypeEnum;
import com.evil.common.core.entity.Order;
import com.evil.common.core.entity.PageModel;
import com.evil.common.core.enums.BaseEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.NumberUtils;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.es.dto.Summary;
import com.evil.common.es.dto.application.order.ESOrderDataDTO;
import com.evil.common.es.dto.application.order.data.ESControlDataDTO;
import com.evil.common.es.dto.application.order.data.ESControlDataValueDTO;
import com.evil.common.es.dto.application.order.node.ESNodeHandlerDTO;
import com.evil.common.es.util.ElasticsearchUtil;
import com.evil.common.family.constant.AppConstant;
import com.evil.common.family.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.github.pagehelper.PageInfo;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.Settings;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 搜索引擎
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@Service
@AllArgsConstructor
public class ElasticsearchServiceImpl implements ElasticsearchService {

    private final ApplicationFormControlService applicationFormControlService;

    private final ElasticsearchTemplate elasticsearchTemplate;

    // region index

    /**
     * 创建索引
     */
    @Override
    public void createIndex() {
        IndexOperations indexOperations = elasticsearchTemplate.indexOps(ESOrderDataDTO.class);
        indexOperations.delete();
        Settings settings = indexOperations.createSettings();
        // 返回最大行数
        settings.put("index.max_result_window", "65536");

        Document mapping = indexOperations.createMapping();
        // 关闭 日期自动检测
        mapping.put("date_detection", false);
//        mapping.put("total_fields.limit", 3000);

        indexOperations.create(settings, mapping);
        indexOperations.refresh();
    }

    /**
     * 删除索引
     */
    @Override
    public void deleteIndex() {
        IndexOperations indexOperations = elasticsearchTemplate.indexOps(ESOrderDataDTO.class);
        indexOperations.delete();
    }

    // endregion

    // region select

    /**
     * ES：是否存在该控件值订单
     *
     * @param controlDataDTO controlDataDTO
     * @return boolean
     */
    @Override
    public boolean isExistOrder(ControlDataDTO controlDataDTO) {
        // 创建bool多条件查询
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        // 表格
        if (ControlCodeEnum.TABLE.getCode().equals(controlDataDTO.getControlCode())) {
            // 创建bool多条件查询
            BoolQuery.Builder rowsQueryBuilder = new BoolQuery.Builder();
            controlDataDTO.getTableValues().forEach(row -> {
                // 创建bool多条件查询
                BoolQuery.Builder columnsQueryBuilder = new BoolQuery.Builder();
                row.forEach(column -> this.queryByControlData(columnsQueryBuilder, column));
                rowsQueryBuilder.should(columnsQueryBuilder.build()._toQuery());
            });
            boolQueryBuilder.must(rowsQueryBuilder.build()._toQuery());
        }
        // 非表格
        else {
            this.queryByControlData(boolQueryBuilder, controlDataDTO);
        }
        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder()
                .withQuery(boolQueryBuilder.build()._toQuery())
                .withTrackTotalHits(true);
        return !this.orderDataList(nativeQueryBuilder.build()).isEmpty();
    }

    /**
     * 订单数据
     *
     * @param orderId orderId
     * @return OrderPageDataRespDTO
     */
    @Override
    public ESOrderDataDTO esOrder(Long orderId) {
        String id = Long.toString(orderId);
        ESOrderDataDTO esOrderDataDTO = elasticsearchTemplate.get(id, ESOrderDataDTO.class);
        if (Objects.isNull(esOrderDataDTO)) {
            throw new BusinessException(RCodeEnum.APPLICATION_ORDER_NOT_EXIST);
        }
        return esOrderDataDTO;
    }

    /**
     * 订单数据
     *
     * @param orderId orderId
     * @return OrderPageDataRespDTO
     */
    @Override
    public OrderPageDataRespDTO order(Long orderId) {
        return this.esOrder(orderId).toOrderPageDataRespDTO();
    }

    /**
     * 订单数据
     *
     * @param orderId orderId
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ControlDataDTO> orderDataList(Long orderId) {
        return this.order(orderId).getData();
    }

    /**
     * 订单数据列表
     *
     * @param orderIds orderIds
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ESOrderDataDTO> esOrders(Long... orderIds) {
        return this.esOrders(Arrays.asList(orderIds));
    }

    /**
     * 订单数据列表
     *
     * @param orderIds orderIds
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ESOrderDataDTO> esOrders(Collection<Long> orderIds) {
        List<String> ids = StreamUtil.transListT(orderIds, e -> Long.toString(e));
        if (CollectionUtil.isEmpty(ids)) {
            return new ArrayList<>();
        }
        // 创建bool多条件查询
        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder()
                .withQuery(e -> e.bool(b -> b.must(bb -> bb.ids(cc -> cc.values(ids)))))
                .withTrackTotalHits(true);
        SearchHits<ESOrderDataDTO> searchHits = elasticsearchTemplate.search(nativeQueryBuilder.build(), ESOrderDataDTO.class);
        return searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    /**
     * 订单数据列表
     *
     * @param applicationKey 应用key
     * @param orderSns       订单编号
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ESOrderDataDTO> esOrders(String applicationKey, List<String> orderSns) {
        if (CollectionUtil.isEmpty(orderSns)) {
            return new ArrayList<>();
        }
        // 创建bool多条件查询
        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder()
                .withQuery(e -> e.bool(b -> {
                    b.must(bb -> bb.term(cc -> cc.field(ESOrderDataDTO.COL_AK).value(applicationKey)));
                    b.must(bb -> bb.terms(cc -> cc.field(ESOrderDataDTO.COL_OS).terms(dd -> dd.value(StreamUtil.transListT(orderSns, FieldValue::of)))));
                    return b;
                }))
                .withTrackTotalHits(true);
        SearchHits<ESOrderDataDTO> searchHits = elasticsearchTemplate.search(nativeQueryBuilder.build(), ESOrderDataDTO.class);
        return searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    /**
     * 订单数据列表
     *
     * @param applicationKey 应用key
     * @param orderSns       订单编号
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ESOrderDataDTO> esOrders(String applicationKey, String... orderSns) {
        return this.esOrders(applicationKey, ListUtil.toList(orderSns));
    }

    /**
     * 订单ids
     *
     * @param orderPageReqDTO orderPageReqDTO
     * @return List
     */
    @Override
    public List<Long> orderIds(OrderPageReqDTO orderPageReqDTO) {
        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder()
                .withQuery(this.createBoolQueryBuilder(orderPageReqDTO))
                .withFields(ESOrderDataDTO.COL_ID)
                .withTrackTotalHits(true);

        SearchHits<ESOrderDataDTO> searchHits = elasticsearchTemplate.search(nativeQueryBuilder.build(), ESOrderDataDTO.class);
        return searchHits.stream().map(e -> e.getContent().getOrderId()).collect(Collectors.toList());
    }

    /**
     * 订单数据列表
     *
     * @param orderPageReqDTO orderPageReqDTO
     * @return List
     */
    @Override
    public List<OrderPageDataRespDTO> orders(OrderPageReqDTO orderPageReqDTO) {
        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder()
                .withQuery(this.createBoolQueryBuilder(orderPageReqDTO))
                .withTrackTotalHits(true);

        return this.orderDataList(nativeQueryBuilder.build());
    }

    /**
     * 关联订单数据列表
     *
     * @param pageModel pageModel
     * @return List
     */
    @Override
    public <T> T relatedDataList(PageModel<RelatedBaseReqDTO<T>> pageModel) {
        RelatedBaseReqDTO<T> base = pageModel.getParam();

        String title = "related";
        // 构造query
        SearchRequest.Builder queryBuilder = new SearchRequest.Builder();
        queryBuilder.trackTotalHits(e -> e.enabled(true));

        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        // 应用id
        boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_AI).query(base.getApplicationId())));
        // 筛选条件
        this.handleJudgeOrs(boolQueryBuilder, base.getJudgeOrs(), base.getForceUseValue());
        queryBuilder.query(boolQueryBuilder.build()._toQuery());
        // 单控件数据查询 以后考虑策略
        if (base instanceof RelatedDataListReqDTO param) {
            ControlDTO control = param.getControl();
            // 关键字
            if (StringUtils.isNotBlank(base.getKeyword())) {
                BoolQuery.Builder keywordQuery = new BoolQuery.Builder();
                // 关键字-控件
                keywordQuery.must(b -> b.match(bm -> bm.field(ESControlDataDTO.COL_DN_CI).query(control.getControlId())));
                // 关键字-值
                QueryVariant keywordValueQuery = this.handleKeyword(base.getKeyword(), base.getFuzzyQuery(), ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD);
                // 关键字-值-nested
                this.reversalQuery(false, keywordQuery, keywordValueQuery);
                // 关键字-控件-nested
                boolQueryBuilder.must(b -> b.nested(bn -> bn.path(ESOrderDataDTO.COL_DN).query(keywordQuery.build()._toQuery()).scoreMode(ChildScoreMode.None)));
            }

            // 汇总查询--不支持分页
            if (null != control && Objects.nonNull(param.getSummaryType())) {
                queryBuilder.size(0);
                AggTrees aggTrees = AggTrees.builder().build();
                ReportingTargetDTO target = new ReportingTargetDTO(title, control.getControlId(), control.getControlTitle(), param.getSummaryType());
                // 处理聚合
                this.handleAgg(target, aggTrees);
                // 转化聚合树请求
                this.toAggregations(aggTrees.aggs, queryBuilder, null);
                // 封装聚合查询
                Aggregate aggregate = this.queryAgg(queryBuilder.build());
                // 解析汇总结果
                Map<Summary<String>, Aggregate> result = ElasticsearchUtil.aggregationsData(new RelatedSummary(title, null), aggregate);
                // 转换返回结构
                ControlDataDTO controlDataDTO = ApplicationUtil.controlToControlData(control);
                controlDataDTO.setValues(result.keySet().stream().map(e -> {
                    String value = e.getValue();
                    // 需要处理精度
                    if (control instanceof NumberControlDTO && Objects.nonNull(((NumberControlDTO) control).getDecimalAccuracy())) {
                        BigDecimal bigDecimal = NumberUtils.toBigDecimal(e.getValue(), null);
                        value = bigDecimal.setScale(((NumberControlDTO) control).getDecimalAccuracy(), RoundingMode.HALF_UP).toPlainString();
                    }
                    return new ControlDataValueDTO(value);
                }).collect(Collectors.toList()));

                param.setResult(new RelatedDataListDTO(controlDataDTO));
            }
            // 单控件关联查询--不支持分页
            else if (null != control) {
                queryBuilder.size(0);
                AggTrees aggTrees = AggTrees.builder().build();
                AggBaseDTO aggBaseDTO = new AggBaseDTO(control.getControlId(), title);
                // 处理聚合
                this.handleAgg(aggBaseDTO, aggTrees);
                // 转化聚合树请求
                this.toAggregations(aggTrees.aggs, queryBuilder, null);
                // 封装聚合查询
                Aggregate aggregate = this.queryAgg(queryBuilder.build());
                // 解析汇总结果
                Map<Summary<String>, Aggregate> result = ElasticsearchUtil.aggregationsData(new RelatedSummary(title, null), aggregate);
                // 转换返回结构
                RelatedDataListDTO controlDataDTOS = new RelatedDataListDTO();
                result.keySet().forEach(summary -> {
                    ControlDataDTO controlDataDTO = ApplicationUtil.controlToControlData(control);
                    String value = summary.getValue();
                    // 需要处理精度
                    if (control instanceof NumberControlDTO && Objects.nonNull(((NumberControlDTO) control).getDecimalAccuracy())) {
                        BigDecimal bigDecimal = NumberUtils.toBigDecimal(summary.getValue(), null);
                        value = bigDecimal.setScale(((NumberControlDTO) control).getDecimalAccuracy(), RoundingMode.HALF_UP).toPlainString();
                    }
                    controlDataDTO.setValues(ListUtil.toList(new ControlDataValueDTO(value)));

                    controlDataDTOS.add(controlDataDTO);
                });

                param.setResult(controlDataDTOS);
            }
        }
        // 多控件数据查询 以后考虑策略
        if (base instanceof RelatedOrderListReqDTO param) {
            Collection<ControlDTO> controls = param.getControls();
            // 关键字 TODO

        }
        return base.get();
    }

    /**
     * 订单数据分页
     *
     * @param pageModel pageModel
     * @return PageInfo
     */
    @Override
    public PageInfo<OrderPageDataRespDTO> orderDataPage(PageModel<OrderPageReqDTO> pageModel) {
        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder()
                .withQuery(this.createBoolQueryBuilder(pageModel.getParam()))
                .withPageable(PageRequest.of(pageModel.getPageNum() - 1, pageModel.getPageSize()))
                .withTrackTotalHits(true);
        // 处理排序
        this.handleSort(nativeQueryBuilder, pageModel);
        // 查询
        return this.orderDataPage(nativeQueryBuilder.build());
    }

    /**
     * 报表订单数据分页
     *
     * @param reportingDataListReqDTO reportingDataListReqDTO
     * @return PageInfo
     */
    @Override
    public PageInfo<OrderPageDataRespDTO> orderDataPage(ReportingDataListReqDTO reportingDataListReqDTO) {
        PageModel<OrderPageReqDTO> pageModel = reportingDataListReqDTO.getPageModel();
        return this.orderDataPage(pageModel);
    }

    /**
     * 报表订单数据 汇总
     *
     * @param reportingDTO reportingDTO
     */
    @Override
    public Aggregate orderDataSummary(ApplicationReportingDTO reportingDTO) {
        // 创建bool多条件查询
        BoolQuery.Builder boolQuery = new BoolQuery.Builder();
        // 应用id
        boolQuery.must(m -> m.term(mt -> mt.field(ESOrderDataDTO.COL_AI).value(reportingDTO.getApplicationId())));
        // 报表汇总过滤草稿
        boolQuery.mustNot(m -> m.term(mt -> mt.field(ESOrderDataDTO.COL_PS).value(OrderProcessStatusEnum.DRAFT_BOX.getId())));
        // 筛选条件
        if (SwitchEnum.isOpen(reportingDTO.getFilterStatus())) {
            FilterConfigDTO filterConfig = reportingDTO.getFilterConfig();
            this.handleJudgeOrs(boolQuery, filterConfig.getJudgeOrs(), false);
        }

        SearchRequest.Builder searchRequestBuild = new SearchRequest.Builder()
                .query(boolQuery.build()._toQuery())
                .size(0);
        this.toAggregations(this.createAggregationBuilder3(reportingDTO), searchRequestBuild, null);
        // 封装聚合查询
        return this.queryAgg(searchRequestBuild.build());
    }

    // endregion

    // region save

    /**
     * 保存订单数据
     *
     * @param esOrderDataDTO esOrderDataDTO
     */
    @Override
    public void save(ESOrderDataDTO esOrderDataDTO) {
        elasticsearchTemplate.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
        try {
            elasticsearchTemplate.save(esOrderDataDTO);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(RCodeEnum.ELASTICSEARCH_ERROR);
        }
    }

    /**
     * 保存订单
     *
     * @param order           applicationOrder
     * @param app             applicationInfo
     * @param controlDataList controlDataList
     */
    @Override
    public void save(ApplicationInfo app, ApplicationOrder order, List<ControlDataDTO> controlDataList) {
        ESOrderDataDTO esOrderDataDTO = this.initESOrder(app, order, controlDataList, new ArrayList<>());
        this.save(esOrderDataDTO);
    }

    /**
     * 保存订单
     *
     * @param app             app
     * @param order           order
     * @param controlDataList controlDataList
     * @param nodeHandlers    nodeHandlers
     */
    @Override
    public void save(ApplicationInfo app, ApplicationOrder order, List<ControlDataDTO> controlDataList, List<ESNodeHandlerDTO> nodeHandlers) {
        ESOrderDataDTO esOrderDataDTO = this.initESOrder(app, order, controlDataList, nodeHandlers);
        this.save(esOrderDataDTO);
    }

    /**
     * 局部更新订单数据（不支持更新表格里面的局部数据，允许整个表格更新）
     *
     * @param order          order
     * @param controlDataMap controlDataMap
     */
    @Override
    public void save(ApplicationOrder order, Map<Long, ControlDataDTO> controlDataMap) {
        ESOrderDataDTO esOrder = this.esOrder(order.getOrderId());
        esOrder.setUpdated(order.getUpdated());
        esOrder.setModifierId(order.getModifierId());
        this.save(esOrder, controlDataMap);
    }

    /**
     * 局部更新订单数据（不支持更新表格里面的局部数据，允许整个表格更新）
     *
     * @param esOrder           esOrder
     * @param controlDataDTOMap controlDataDTOMap
     */
    @Override
    public void save(ESOrderDataDTO esOrder, Map<Long, ControlDataDTO> controlDataDTOMap) {
        Map<Long, ESControlDataDTO> esControlDataMap = StreamUtil.toOrderlyMapK(esOrder.getData(), ESControlDataDTO::getControlId);
        BiFunction<String, ControlDataValueDTO, String> getDataValue = (code, value) -> ControlCodeEnum.findByCode(code).getDataValueToString().apply(value);
        controlDataDTOMap.forEach((controlId, data) -> {
            ESControlDataDTO esControlDataDTO = new ESControlDataDTO(data, getDataValue);
            if (esControlDataMap.containsKey(controlId)) {
                esControlDataMap.replace(controlId, esControlDataDTO);
            } else {
                esControlDataMap.put(controlId, esControlDataDTO);
            }
        });
        esOrder.setData(new ArrayList<>(esControlDataMap.values()), code -> ControlCodeEnum.TABLE.getCode().equals(code));

        this.save(esOrder);
    }

    /**
     * 保存订单数据
     *
     * @param esOrders esOrders
     */
    @Override
    public void saveBatch(Collection<ESOrderDataDTO> esOrders) {
        elasticsearchTemplate.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
        try {
            CollectionUtil.split(esOrders, 100).forEach(elasticsearchTemplate::save);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(RCodeEnum.ELASTICSEARCH_ERROR);
        }
    }

    /**
     * 保存订单数据
     *
     * @param saveESOrders saveESOrders
     */
    @Override
    public void saveBatch(List<SaveESOrderDTO> saveESOrders) {
        List<ESOrderDataDTO> osOrders = StreamUtil.transListT(saveESOrders, e -> this.initESOrder(e.getApp(), e.getOrder(), e.getData(), e.getNodeHandlers()));
        this.saveBatch(osOrders);
    }

    /**
     * 局部更新订单数据（不支持更新表格里面的局部数据，允许整个表格更新）
     *
     * @param orderMap      orderMap
     * @param ordersDataMap ordersDataMap
     */
    @Override
    public void refreshBatch(Map<Long, ApplicationOrder> orderMap, Map<Long, List<ControlDataDTO>> ordersDataMap) {
        List<ESOrderDataDTO> esOrderDataDTOS = this.esOrders(orderMap.keySet());
        Map<Long, ESOrderDataDTO> esOrderMap = StreamUtil.toMapK(esOrderDataDTOS, ESOrderDataDTO::getOrderId);
        ordersDataMap.forEach((orderId, data) -> {
            if (!orderMap.containsKey(orderId) || !esOrderMap.containsKey(orderId)) {
                throw new BusinessException(BaseEnum.RUN_ERROR);
            }
            ApplicationOrder order = orderMap.get(orderId);
            ESOrderDataDTO esOrder = esOrderMap.get(orderId);
            esOrder.setUpdated(order.getUpdated());
            esOrder.setModifierId(order.getModifierId());
            this.handleESOrderData(esOrder, data);
        });

        this.saveBatch(esOrderDataDTOS);
    }

    /**
     * 局部更新订单数据（不支持更新表格里面的局部数据，允许整个表格更新）
     *
     * @param orderDataMap orderDataMap
     */
    @Override
    public void refreshBatch(Map<ApplicationOrder, List<ControlDataDTO>> orderDataMap) {
        List<ESOrderDataDTO> esOrders = this.esOrders(StreamUtil.transListT(orderDataMap.keySet(), ApplicationOrder::getOrderId));
        Map<Long, ESOrderDataDTO> esOrderMap = StreamUtil.toMapK(esOrders, ESOrderDataDTO::getOrderId);

        orderDataMap.forEach((order, data) -> {
            if (!esOrderMap.containsKey(order.getOrderId())) {
                throw new BusinessException(BaseEnum.RUN_ERROR);
            }
            ESOrderDataDTO esOrder = esOrderMap.get(order.getOrderId());
            esOrder.setUpdated(order.getUpdated());
            esOrder.setModifierId(order.getModifierId());
            this.handleESOrderData(esOrder, data);
        });

        this.saveBatch(esOrders);
    }
    // endregion

    // region del

    /**
     * 删除订单数据
     *
     * @param refreshAppOrderReqDTO refreshAppOrderReqDTO
     */
    @Override
    public void delete(RefreshAppOrderReqDTO refreshAppOrderReqDTO) {
        log.info("【删除订单数据】，参数：{}", JSON.toJSONString(refreshAppOrderReqDTO));
        elasticsearchTemplate.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
        ByQueryResponse queryResponse = elasticsearchTemplate.delete(this.handle(refreshAppOrderReqDTO), ESOrderDataDTO.class);
        log.info("【删除订单数据】，参数：{}，结果：{}", JSON.toJSONString(refreshAppOrderReqDTO), JSON.toJSON(queryResponse));
    }

    /**
     * 删除订单数据
     *
     * @param orderIds orderId
     */
    @Override
    public void delete(Long... orderIds) {
        List<String> deleteIds = Arrays.stream(orderIds).map(e -> Long.toString(e)).collect(Collectors.toList());
        CollectionUtil.split(deleteIds, 50).forEach(list -> this.delete(list.toArray(new String[]{})));
    }

    /**
     * 删除订单数据
     *
     * @param orderIds orderIds
     */
    @Override
    public void delete(Collection<Long> orderIds) {
        this.delete(orderIds.stream().map(id -> Long.toString(id)).toArray(String[]::new));
    }

    /**
     * 删除订单数据
     *
     * @param ids ids
     */
    @Override
    public void delete(String... ids) {
        if (ids.length < 1) {
            return;
        }

        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder()
                .withQuery(Query.of(builder -> builder.ids(idsBuilder -> idsBuilder.values(ListUtil.toList(ids)))));
        try {
            log.info("【删除ES订单数据】，订单：{}", JSON.toJSONString(ids));
            elasticsearchTemplate.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
            ByQueryResponse response = elasticsearchTemplate.delete(nativeQueryBuilder.build(), ESOrderDataDTO.class);
            log.info("【删除ES订单数据】，订单：{}，结果：{}", ids, JSON.toJSONString(response));
        } catch (Exception e) {
            log.error("【删除ES订单数据】失败，订单：{}", JSON.toJSONString(ids), e);
            throw new BusinessException(RCodeEnum.ELASTICSEARCH_ERROR);
        }
    }
    // endregion

    /**
     * 初始化搜索引擎订单数据
     *
     * @param order           applicationOrder
     * @param app             applicationInfo
     * @param controlDataList controlDataList
     * @param nodeHandlers    nodeHandlers
     * @return ESOrderDataDTO
     */
    @Override
    public ESOrderDataDTO initESOrder(ApplicationInfo app, ApplicationOrder order, List<ControlDataDTO> controlDataList, List<ESNodeHandlerDTO> nodeHandlers) {
        ESOrderDataDTO esOrder = new ESOrderDataDTO();
        esOrder.setApplicationKey(app.getApplicationKey());
        esOrder.setApplicationId(order.getApplicationId());
        esOrder.setEnterpriseId(order.getEnterpriseId());
        esOrder.setOrderId(order.getOrderId());
        esOrder.setOrderSn(order.getOrderSn());
        esOrder.setProcessStatus(order.getProcessStatus());
        esOrder.setCreatorId(order.getCreatorId());
        esOrder.setModifierId(order.getModifierId());
        esOrder.setCreated(order.getCreated());
        esOrder.setUpdated(order.getUpdated());

        this.handleESOrderData(esOrder, controlDataList);
        esOrder.setNodeHandlers(nodeHandlers);

        return esOrder;
    }

    /**
     * 构建ES节点处理人实体
     *
     * @param nodeHandlers nodeHandlers
     * @return ESOrderDataDTO
     */
    @Override
    public List<ESNodeHandlerDTO> initESNodeHandler(List<ApplicationOrderNodeHandler> nodeHandlers) {
        return StreamUtil.transListT(nodeHandlers, nh -> BeanUtil.copyProperties(nh, ESNodeHandlerDTO.class));
    }

    /**
     * 处理ES 订单数据
     *
     * @param esOrderDataDTO  esOrderDataDTO
     * @param controlDataList controlDataList
     */
    private void handleESOrderData(ESOrderDataDTO esOrderDataDTO, List<ControlDataDTO> controlDataList) {
        BiFunction<String, ControlDataValueDTO, String> getDataValue = (code, value) -> ControlCodeEnum.findByCode(code).getDataValueToString().apply(value);
        esOrderDataDTO.setData(ESControlDataDTO.toESControlDataDTO(controlDataList, getDataValue), code -> ControlCodeEnum.TABLE.getCode().equals(code));
    }

    /**
     * 列表查询
     *
     * @param query query
     * @return List
     */
    private List<OrderPageDataRespDTO> orderDataList(NativeQuery query) {
        SearchHits<ESOrderDataDTO> searchHits = elasticsearchTemplate.search(query, ESOrderDataDTO.class);
        return searchHits.stream().map(e -> e.getContent().toOrderPageDataRespDTO()).collect(Collectors.toList());
    }

    /**
     * 分页查询
     *
     * @param query query
     * @return PageInfo
     */
    private PageInfo<OrderPageDataRespDTO> orderDataPage(NativeQuery query) {
        SearchHits<ESOrderDataDTO> searchHits = elasticsearchTemplate.search(query, ESOrderDataDTO.class);

        SearchPage<ESOrderDataDTO> page = SearchHitSupport.searchPageFor(searchHits, query.getPageable());
        PageInfo<OrderPageDataRespDTO> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page.getNumber() + 1);
        pageInfo.setPageSize(page.getSize());
        pageInfo.setPages(page.getTotalPages());
        pageInfo.setTotal(page.getTotalElements());
        pageInfo.setList(page.getContent().stream().map(e -> e.getContent().toOrderPageDataRespDTO()).toList());
        pageInfo.calcByNavigatePages(PageInfo.DEFAULT_NAVIGATE_PAGES);
        return pageInfo;
    }

    /**
     * 构造查询条件
     *
     * @param orderPageReqDTO orderPageReqDTO
     * @return QueryBuilder
     */
    private Query createBoolQueryBuilder(OrderPageReqDTO orderPageReqDTO) {
        // 创建bool多条件查询
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        // 企业id
        if (Objects.nonNull(orderPageReqDTO.getEnterpriseId())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_EI).query(orderPageReqDTO.getEnterpriseId())));
        }
        // 应用key
        if (Objects.nonNull(orderPageReqDTO.getApplicationId())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_AI).query(orderPageReqDTO.getApplicationId())));
        }
        // 应用key
        if (StringUtils.isNotBlank(orderPageReqDTO.getApplicationKey())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_AK).query(orderPageReqDTO.getApplicationKey())));
        }
        // 流程状态
        if (Objects.nonNull(orderPageReqDTO.getProcessStatus())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_PS).query(orderPageReqDTO.getProcessStatus())));
        }
        // 流程状态
        if (Objects.nonNull(orderPageReqDTO.getNotEqProcessStatus())) {
            boolQueryBuilder.mustNot(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_PS).query(orderPageReqDTO.getNotEqProcessStatus())));
        }
        // 流程状态
        if (CollectionUtil.isNotEmpty(orderPageReqDTO.getInProcessStatus())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_PS).query(FieldValue.of(JsonData.of(orderPageReqDTO.getInProcessStatus())))));
        }
        // 发起人
        if (Objects.nonNull(orderPageReqDTO.getCreatorId())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_CI).query(orderPageReqDTO.getCreatorId())));
        }
        // 节点处理人 （或 处理）
        if (CollectionUtil.isNotEmpty(orderPageReqDTO.getNodeHandlers())) {
            BoolQuery.Builder nodeHandlersQuery = new BoolQuery.Builder();
            orderPageReqDTO.getNodeHandlers().forEach(nodeHandler -> {
                BoolQuery.Builder nodeHandlerQuery = new BoolQuery.Builder();
                boolean has = false;
                if (Objects.nonNull(nodeHandler.getNodeId())) {
                    nodeHandlerQuery.must(b -> b.term(bt -> bt.field(ESNodeHandlerDTO.COL_NODE_HANDLERS_NESTED_NODE_ID).value(nodeHandler.getNodeId())));
                    has = true;
                }
                if (Objects.nonNull(nodeHandler.getHandlerId())) {
                    nodeHandlerQuery.must(b -> b.term(bt -> bt.field(ESNodeHandlerDTO.COL_NODE_HANDLERS_NESTED_HANDLER_ID).value(nodeHandler.getHandlerId())));
                    has = true;
                }
                if (Objects.nonNull(nodeHandler.getHandleType())) {
                    nodeHandlerQuery.must(b -> b.term(bt -> bt.field(ESNodeHandlerDTO.COL_NODE_HANDLERS_NESTED_HANDLE_TYPE).value(nodeHandler.getHandleType())));
                    has = true;
                }
                if (has) {
                    nodeHandlersQuery.should(b -> b.nested(bn -> bn.path(ESOrderDataDTO.COL_NHN).query(nodeHandlerQuery.build()._toQuery()).scoreMode(ChildScoreMode.None)));
                }
            });
            boolQueryBuilder.must(e -> e.bool(nodeHandlersQuery.build()));
        }
        // 全局关键字
        if (StringUtils.isNotBlank(orderPageReqDTO.getKeyword())) {
            QueryVariant keywordQuery = this.handleKeyword(orderPageReqDTO.getKeyword(), orderPageReqDTO.getFuzzyQuery(), ESControlDataValueDTO.COL_DN_V_DV_KEYWORD);
            boolQueryBuilder.must(b -> b.nested(bn -> bn.path(ESOrderDataDTO.COL_DN).query(keywordQuery._toQuery()).scoreMode(ChildScoreMode.None)));
        }

        // 筛选参数
        this.handleQuery(boolQueryBuilder, orderPageReqDTO.getQueries());
        // 筛选条件
        this.handleJudgeOrs(boolQueryBuilder, orderPageReqDTO.getJudgeOrs(), orderPageReqDTO.getForceUseValue());
        return boolQueryBuilder.build()._toQuery();
    }

    /**
     * 构造查询条件
     *
     * @param reportingDataListReqDTO reportingDataListReqDTO
     * @return QueryBuilder
     */
    private BoolQuery.Builder createBoolQueryBuilder(ReportingDataListReqDTO reportingDataListReqDTO) {
        PageModel<OrderPageReqDTO> pageModel = reportingDataListReqDTO.getPageModel();
        List<AccurateQueryReqDTO> accurateQueries = reportingDataListReqDTO.getAccurateQueries();

        // 创建bool多条件查询
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        boolQueryBuilder.must(this.createBoolQueryBuilder(pageModel.getParam()));
        boolQueryBuilder.mustNot(b -> b.term(bt -> bt.field(ESOrderDataDTO.COL_PS).value(OrderProcessStatusEnum.DRAFT_BOX.getId())));

        boolean fuzzy = QueryTypeEnum.isFuzzy(reportingDataListReqDTO.getQueryType());
        accurateQueries.forEach(accurateQuery -> {
            // 创建bool多条件查询
            BoolQuery.Builder boolQuery = new BoolQuery.Builder();

            boolQuery.must(b -> b.match(bm -> bm.field(ESControlDataDTO.COL_DN_CI).query(accurateQuery.getControlId())));
            if (fuzzy) {
                boolQuery.must(b -> b.fuzzy(bm -> bm.field(ESControlDataValueDTO.COL_DN_VN_VALUE_KEYWORD).value(accurateQuery.getSearchKey())));
            } else {
                boolQuery.must(b -> b.match(bm -> bm.field(ESControlDataValueDTO.COL_DN_VN_VALUE_KEYWORD).query(accurateQuery.getSearchKey())));
            }
            boolQueryBuilder.must(b -> b.bool(boolQuery.build()));
        });

        return boolQueryBuilder;
    }

    /**
     * 封装聚合查询
     *
     * @param searchRequest searchRequest
     * @return Aggregate
     */
    private Aggregate queryAgg(SearchRequest searchRequest) {
        SearchResponse<ESOrderDataDTO> execute = elasticsearchTemplate.execute(e -> e.search(searchRequest, ESOrderDataDTO.class));
        Map<String, Aggregate> aggregations = execute.aggregations();
        return NestedAggregate.of(e -> e.aggregations(aggregations).docCount(aggregations.size()))._toAggregate();
    }

    /**
     * 构造报表汇总条件
     *
     * @param reportingDTO reportingDTO
     */
    private List<Agg> createAggregationBuilder3(ApplicationReportingDTO reportingDTO) {
        AggTrees aggTrees = AggTrees.builder().build();
        for (ReportingHeaderDTO header : reportingDTO.getReportingHeaders()) {
            this.handleAgg(header, aggTrees);
        }
        for (ReportingHeaderDTO header : reportingDTO.getReportingColumnHeaders()) {
            this.handleAgg(header, aggTrees);
        }
        for (ReportingTargetDTO target : reportingDTO.getReportingTargets()) {
            // 数据总量 统计订单号数量
            if (ReportingTargetTypeEnum.DATA_SUMMARY.getId() == target.getTargetType()) {
                target.setControlId(OrderSpecialControlEnum.SN.getId());
                target.setSummaryType(SummaryTypeEnum.COUNT.getId());
            }
            this.handleAgg(target, aggTrees);
        }
        if (null != aggTrees.root) {
            aggTrees.aggs.add(aggTrees.root);
        }
        return aggTrees.aggs;
    }

    /**
     * 处理聚合
     *
     * @param aggBase  aggBase
     * @param aggTrees aggTrees
     */
    private void handleAgg(AggBaseDTO aggBase, AggTrees aggTrees) {
        AggField aggField = this.getAggField(aggBase, aggTrees.preBeingTableControl);
        // value层
        Agg valueAgg;
        String value = ElasticsearchUtil.value(aggBase.getTitle());
        // 有汇总方式：结束了，则运算处理
        if (aggBase instanceof ReportingTargetDTO target) {
            valueAgg = new AggSummary(value)
                    .setSummary(new AggSummary.Summary(aggField.valueField, target.getSummaryType(), target.getDecimal()));
        }
        // 没有汇总方式：还没结束，则分桶处理
        else if (aggBase instanceof ReportingHeaderDTO header) {
            // 时间格式化
            if (null != header.getDateType()) {
                valueAgg = new AggTerms(ElasticsearchUtil.value(aggBase.getTitle()))
                        .setTerms(new AggTerms.Terms(aggField.valueField, "", header.getDateType(), AggTerms.TERMS_SIZE));
            } else {
                valueAgg = new AggTerms(ElasticsearchUtil.value(aggBase.getTitle()))
                        .setTerms(new AggTerms.Terms(aggField.valueField));
            }
        }
        // 没有汇总方式：还没结束，则分桶处理
        else {
            valueAgg = new AggTerms(ElasticsearchUtil.value(aggBase.getTitle()))
                    .setTerms(new AggTerms.Terms(aggField.valueField));
        }
        // control层
        Agg controlAgg = new AggFilter(ElasticsearchUtil.control(aggBase.getTitle()))
                .setFilter(new AggFilter.Filter<>(aggField.controlField, aggBase.getControlId()))
                .sub(valueAgg);
        // nested层
        Agg agg = new AggNested(ElasticsearchUtil.nested(aggBase.getTitle()))
                .setNested(new AggNested.Nested(aggField.nestedField))
                .sub(controlAgg);
        if (aggBase instanceof ReportingHeaderDTO header) {
            if (null != aggTrees.last) {
                // 用上一层的title做rev
                AggRev aggRev = new AggRev(ElasticsearchUtil.rev(aggTrees.lastTitle));
                aggRev.sub(agg);
                aggTrees.last.sub(aggRev);
            }
            aggTrees.last = valueAgg;
            if (null == aggTrees.root) {
                aggTrees.root = agg;
            }
            aggTrees.lastTitle = aggBase.getTitle();
            aggTrees.preBeingTableControl = header.getBeingTableControl();
        } else {
            if (null != aggTrees.last) {
                // 用上一层的title做rev
                String title = ElasticsearchUtil.rev(aggTrees.lastTitle);
                if (!aggTrees.last.getTitle().equals(title)) {
                    AggRev aggRev = new AggRev(title);
                    aggRev.sub(agg);
                    aggTrees.last.sub(aggRev);
                    aggTrees.last = aggRev;
                } else {
                    aggTrees.last.sub(agg);
                }
            } else {
                aggTrees.aggs.add(agg);
            }
        }
    }

    /**
     * 转化聚合树请求
     *
     * @param aggs               aggs
     * @param searchRequestBuild searchRequestBuild
     * @param last               last
     */
    private void toAggregations(List<Agg> aggs, SearchRequest.Builder searchRequestBuild, Aggregation.Builder.ContainerBuilder last) {
        for (Agg agg : aggs) {
            Aggregation.Builder.ContainerBuilder aggregation = agg.toAggregation();
            if (null == aggregation) {
                continue;
            }
            if (!agg.getSubs().isEmpty()) {
                this.toAggregations(agg.getSubs(), searchRequestBuild, aggregation);
            }
            if (null != last) {
                last.aggregations(agg.getTitle(), aggregation.build());
            } else {
                searchRequestBuild.aggregations(agg.getTitle(), aggregation.build());
            }
        }
    }

    private AggField getAggField(AggBaseDTO aggBaseDTO, boolean preBeingTableControl) {
        // 上层为表格层（基于 table_values_nested.row 去汇总）
        if (aggBaseDTO instanceof ReportingHeaderDTO header && preBeingTableControl && header.getBeingTableControl()) {
            return AggField.builder()
                    .controlField(ESControlDataDTO.COL_DN_TVN_R_CI)
                    .nestedField(ESControlDataDTO.COL_DN_TVN_R)
                    .valueField(ESControlDataValueDTO.COL_DN_TVN_R_V_DV_KEYWORD)
                    .build();
        }
        // 当前为表格层（基于 table_values 去汇总）
        else if (aggBaseDTO instanceof ReportingHeaderDTO header && header.getBeingTableControl()) {
            return AggField.builder()
                    .controlField(ESControlDataDTO.COL_DN_TVN_CI)
                    .nestedField(ESControlDataDTO.COL_DN_TVN)
                    .valueField(ESControlDataValueDTO.COL_DN_TVN_V_DV_KEYWORD)
                    .build();
        }
        // 当前为普通层（基于 data_nested 去汇总）
        else {
            return AggField.builder()
                    .controlField(ESControlDataDTO.COL_DN_CI)
                    .nestedField(ESOrderDataDTO.COL_DN)
                    .valueField(ESControlDataValueDTO.COL_DN_V_DV_KEYWORD)
                    .build();
        }
    }

    /**
     * 根据控件值查询
     *
     * @param queryBuilder   queryBuilder
     * @param controlDataDTO controlDataDTO
     */
    private void queryByControlData(BoolQuery.Builder queryBuilder, ControlDataDTO controlDataDTO) {
        BoolQuery.Builder b = new BoolQuery.Builder();
        ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(controlDataDTO.getControlCode());
        // 表单控件id
        b.must(bm -> bm.term(bmt -> bmt.field(ESControlDataDTO.COL_DN_CI).value(controlDataDTO.getControlId())));
        // 数据id
        if (Objects.nonNull(controlDataDTO.getOrderDataId())) {
            b.mustNot(bm -> bm.term(bmt -> bmt.field(ESControlDataDTO.COL_DN_ODI).value(controlDataDTO.getOrderDataId())));
        }

        String key = controlCodeEnum.valueUseId() ? ESControlDataValueDTO.COL_DN_VN_ID : ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD;
        List<String> values = OrderDataUtil.getControlValues(controlCodeEnum, controlDataDTO.getValues());

        TermsQuery.Builder terms = new TermsQuery.Builder().field(key).terms(t -> t.value(StreamUtil.transListT(values, FieldValue::of)));
        this.reversalQuery(false, b, terms.build());
        queryBuilder.must(r -> r.nested(nr -> nr.query(b.build()._toQuery()).scoreMode(ChildScoreMode.None)));
    }

    /**
     * 处理查询条件
     *
     * @param boolQueryBuilder boolQueryBuilder
     * @param queries          queries
     */
    private void handleQuery(BoolQuery.Builder boolQueryBuilder, List<OrderQueryReqDTO> queries) {
        // 补充控件code
        List<Long> controlIds = queries.stream().map(OrderQueryReqDTO::getControlId).collect(Collectors.toList());
        Map<Long, String> controlCodeMap = applicationFormControlService.controlCodeMap(controlIds);
        queries.forEach(q -> q.setControlCode(controlCodeMap.getOrDefault(q.getControlId(), q.getControlCode())));

        queries.forEach(query -> {
            BoolQuery.Builder queryBuilder = new BoolQuery.Builder();
            queryBuilder.must(b -> b.term(bt -> bt.field(ESControlDataDTO.COL_DN_CI).value(query.getControlId())));

            // 是否反转
            boolean reversal = StringUtils.isNotBlank(query.getSearchType()) && OrderSearchTypeEnum.BLANK.getType().equals(query.getSearchType());
            // 下拉项查询
            if (CollectionUtil.isNotEmpty(query.getSearchOptions())) {
                TermsQueryField termsQueryField = TermsQueryField.of(t -> t.value(StreamUtil.transListT(query.getSearchOptions(), FieldValue::of)));
                TermsQuery.Builder optionQueryBuilder = new TermsQuery.Builder().field(ESControlDataValueDTO.COL_DN_VN_ID).terms(termsQueryField);
                // 是否反转查询
                this.reversalQuery(reversal, queryBuilder, optionQueryBuilder.build());
            }
            // 范围查询
            else if (SwitchEnum.isOpen(query.getScope())) {
                // 有任意一个最小最大值
                if (StringUtils.isNotBlank(query.getMaxValue()) || StringUtils.isNotBlank(query.getMinValue())) {
                    ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(query.getControlCode());
                    boolean isDate = controlCodeEnum.isDate();
                    String field = ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD;
                    if (isDate) {
                        field = ESControlDataValueDTO.COL_DN_VN_DATE;
                    } else if (controlCodeEnum == ControlCodeEnum.RELATED_SELECT) {
                        ApplicationFormControl control = applicationFormControlService.findById(query.getControlId());
                        if (SwitchEnum.isOpen(control.getSummaryStatus())) {
                            field = ESControlDataValueDTO.COL_DN_VN_DATA;
                        }
                    }
                    RangeQuery.Builder rangeQueryBuilder = new RangeQuery.Builder().field(field);
                    if (StringUtils.isNotBlank(query.getMaxValue())) {
                        // 开闭区间
                        this.handleDate(isDate, query.getMaxValue(), SwitchEnum.isOpen(query.getMaxType()) ? rangeQueryBuilder::lte : rangeQueryBuilder::lt);
                    }
                    if (StringUtils.isNotBlank(query.getMinValue())) {
                        // 开闭区间
                        this.handleDate(isDate, query.getMinValue(), SwitchEnum.isOpen(query.getMaxType()) ? rangeQueryBuilder::gte : rangeQueryBuilder::gt);
                    }
                    // 是否反转查询
                    this.reversalQuery(reversal, queryBuilder, rangeQueryBuilder.build());
                }
            }
            // 关键字查询
            else if (StringUtils.isNotBlank(query.getKeyword())) {
                QueryVariant keywordQuery = this.handleKeyword(query.getKeyword(), query.getFuzzyQuery(), ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD);
                // 是否反转查询
                this.reversalQuery(reversal, queryBuilder, keywordQuery);
            }
            // 是否填值查询
            else {
                ExistsQuery.Builder existsQueryBuilder = new ExistsQuery.Builder().field(ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD);
                // 是否反转查询
                this.reversalQuery(reversal, queryBuilder, existsQueryBuilder.build());
            }
            boolQueryBuilder.must(b -> b.nested(bn -> bn.path(ESOrderDataDTO.COL_DN).query(queryBuilder.build()._toQuery()).scoreMode(ChildScoreMode.None)));
        });
    }

    /**
     * 是否反转查询
     *
     * @param reversal         reversal
     * @param boolQueryBuilder boolQueryBuilder
     * @param queryBuilder     queryBuilder
     */
    private void reversalQuery(boolean reversal, BoolQuery.Builder boolQueryBuilder, QueryVariant queryBuilder) {
        if (reversal) {
            // 反转查询
            boolQueryBuilder.mustNot(r -> r.nested(nr -> nr.path(ESControlDataDTO.COL_DN_VN).query(queryBuilder._toQuery()).scoreMode(ChildScoreMode.None)));
        } else {
            // 非反转查询
            boolQueryBuilder.must(r -> r.nested(nr -> nr.path(ESControlDataDTO.COL_DN_VN).query(queryBuilder._toQuery()).scoreMode(ChildScoreMode.None)));
        }
    }

    private NativeQuery handle(RefreshAppOrderReqDTO refreshAppOrderReqDTO) {
        // 创建bool多条件查询
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        if (Objects.nonNull(refreshAppOrderReqDTO.getEnterpriseId())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_EI).query(refreshAppOrderReqDTO.getEnterpriseId())));
        }
        if (Objects.nonNull(refreshAppOrderReqDTO.getApplicationId())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_AI).query(refreshAppOrderReqDTO.getApplicationId())));
        }
        if (Objects.nonNull(refreshAppOrderReqDTO.getApplicationKey())) {
            boolQueryBuilder.must(b -> b.match(bm -> bm.field(ESOrderDataDTO.COL_AK).query(refreshAppOrderReqDTO.getApplicationKey())));
        }

        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder()
                .withQuery(boolQueryBuilder.build()._toQuery())
                .withTrackTotalHits(true);
        return nativeQueryBuilder.build();
    }

    /**
     * 处理筛选条件
     *
     * @param queryBuilder queryBuilder
     * @param judgeOrs     judgeOrs
     */
    private void handleJudgeOrs(BoolQuery.Builder queryBuilder, List<JudgeOr> judgeOrs, boolean forceUseValue) {
        // 补充控件code
        List<Long> controlIds = new ArrayList<>();
        judgeOrs.forEach(or -> or.getJudgeAnd().forEach(and -> controlIds.add(and.getControlId())));
        Map<Long, String> controlCodeMap = applicationFormControlService.controlCodeMap(controlIds);
        judgeOrs.forEach(or -> or.getJudgeAnd().forEach(and -> and.setControlCode(controlCodeMap.getOrDefault(and.getControlId(), and.getControlCode()))));

        BoolQuery.Builder orsQueryBuilder = new BoolQuery.Builder();
        judgeOrs.forEach(or -> {
            BoolQuery.Builder orQueryBuilder = new BoolQuery.Builder();
            or.getJudgeAnd().forEach(and -> {
                BoolQuery.Builder andQueryBuilder = new BoolQuery.Builder();
                ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(and.getControlCode());
                // 表单控件id
                andQueryBuilder.must(b -> b.term(bt -> bt.field(ESControlDataDTO.COL_DN_CI).value(and.getControlId())));

                boolean valueUseId = controlCodeEnum.valueUseId();
                boolean isDate = controlCodeEnum.isDate();
                String field = valueUseId && !forceUseValue ? ESControlDataValueDTO.COL_DN_VN_ID :
                        (isDate ? ESControlDataValueDTO.COL_DN_VN_DATE : ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD);
                Function<String, FieldValue> valueFormat = valueUseId && !forceUseValue ? e -> FieldValue.of(Long.parseLong(e)) : FieldValue::of;
                TermsQueryField termsQueryField = TermsQueryField.of(e -> e.value(StreamUtil.transListT(and.getValues(), valueFormat)));

                boolean has = false;
                QueryVariant queryVariant;
                if (CollectionUtil.isNotEmpty(and.getValues())) {
                    has = true;
                    queryVariant = new TermsQuery.Builder().field(field).terms(termsQueryField).build();
                } else {
                    queryVariant = new ExistsQuery.Builder().field(field).build();
                }
                JudgeSignEnum signEnum = JudgeSignEnum.isExistById(and.getJudgeSign());
                switch (signEnum) {
                    case EQUALS ->
                        // 等于 不需要反转
                            this.reversalQuery(!has, andQueryBuilder, queryVariant);
                    case UN_EQUALS ->
                        // 不等于 需要反转
                            this.reversalQuery(has, andQueryBuilder, queryVariant);
                    case CONTAIN -> {
                        // 多个值
                        if (controlCodeEnum.isMultiple()) {
                            if (has) {
                                // 有条件
                                this.reversalQuery(false, andQueryBuilder, new TermsQuery.Builder().field(field).terms(termsQueryField).build());
                            }
                        }
                        // 单个值
                        else {
                            if (has) {
                                // 有条件
                                String value = and.getValues().stream().findFirst().orElseThrow(() -> new BusinessException(BaseEnum.RUN_ERROR));
                                WildcardQuery build = new WildcardQuery.Builder().field(field).value(String.format("*%s*", value)).build();
                                this.reversalQuery(false, andQueryBuilder, build);
                            }
                        }
                    }
                    case LT, LTE, GT, GTE -> {
                        // 不支持多个值的比较
                        if (!controlCodeEnum.isMultiple()) {
                            if (has) {
                                // 有条件
                                String value = and.getValues().stream().findFirst().orElseThrow(() -> new BusinessException(BaseEnum.RUN_ERROR));
                                RangeQuery.Builder rangeQueryBuilder = new RangeQuery.Builder().field(field);
                                if (JudgeSignEnum.LT == signEnum) {
                                    this.handleDate(isDate, value, rangeQueryBuilder::lt);
                                } else if (JudgeSignEnum.LTE == signEnum) {
                                    this.handleDate(isDate, value, rangeQueryBuilder::lte);
                                } else if (JudgeSignEnum.GT == signEnum) {
                                    this.handleDate(isDate, value, rangeQueryBuilder::gt);
                                } else {
                                    this.handleDate(isDate, value, rangeQueryBuilder::gte);
                                }
                                this.reversalQuery(false, andQueryBuilder, rangeQueryBuilder.build());
                            }
                        }
                    }
                    default -> throw new BusinessException(BaseEnum.RUN_ERROR);
                }
                orQueryBuilder.must(b -> b.nested(bn -> bn.path(ESOrderDataDTO.COL_DN)
                        .query(andQueryBuilder.build()._toQuery()).scoreMode(ChildScoreMode.None)));
            });
            orsQueryBuilder.should(orQueryBuilder.build()._toQuery());
        });
        queryBuilder.must(orsQueryBuilder.build()._toQuery());
    }

    /**
     * 处理关键字
     *
     * @param keyword    keyword
     * @param fuzzyQuery fuzzyQuery
     */
    private QueryVariant handleKeyword(@NotBlank String keyword, Integer fuzzyQuery, String field) {
        if (SwitchEnum.isOpen(fuzzyQuery)) {
            // 模糊查询
            return new WildcardQuery.Builder().wildcard(String.format("*%s*", keyword)).field(field).build();
        } else {
            // 精准查询
            return new TermQuery.Builder().field(field).value(keyword).build();
        }
    }

    /**
     * 处理日期范围查询
     *
     * @param isDate isDate
     * @param value  value
     * @param query  query
     */
    private void handleDate(boolean isDate, String value, Function<JsonData, RangeQuery.Builder> query) {
        if (isDate) {
            try {
                String s = DateUtil.formatDateTime(DateUtil.parseDateTime(value));
                RangeQuery.Builder rangeQuery = query.apply(JsonData.of(s));
                rangeQuery.format(DatePattern.NORM_DATETIME_PATTERN).timeZone(AppConstant.DEFAULT_TIME_ZONE.toZoneId().getId());
            } catch (Exception e) {
                throw new BusinessException(BaseEnum.ERROR_PARAMETER, "日期范围查询参数错误");
            }
        } else {
            query.apply(JsonData.of(value));
        }
    }

    /**
     * 处理排序
     *
     * @param queryBuilder queryBuilder
     * @param pageModel    pageModel
     */
    private void handleSort(NativeQueryBuilder queryBuilder, PageModel<?> pageModel) {
        List<Long> controlIds = pageModel.getOrders().stream().map(e -> {
            try {
                return Long.valueOf(e.getField());
            } catch (NumberFormatException ex) {
                throw new BusinessException(BaseEnum.ERROR_PARAMETER, "排序参数错误");
            }
        }).collect(Collectors.toList());
        Map<Long, String> controlCodeMap = applicationFormControlService.controlCodeMap(controlIds);

        if (!pageModel.hasOrders()) {
            // 默认更新时间倒序
            pageModel.addOrders(new Order(Long.toString(OrderSpecialControlEnum.UPDATE.getId()), true));
        }
        pageModel.getOrders().stream().distinct().forEach(order -> {
            Long controlId = Long.valueOf(order.getField());
            SortOrder sortOrder = order.isDesc() ? SortOrder.Desc : SortOrder.Asc;
            // 特殊处理-订单编号
            if (OrderSpecialControlEnum.SN.getId().equals(controlId)) {
                queryBuilder.withSort(s -> s.field(sf -> sf.field(ESOrderDataDTO.COL_ID).order(sortOrder)));
            }
            // 特殊处理-更新时间
            else if (OrderSpecialControlEnum.UPDATE.getId().equals(controlId)) {
                queryBuilder.withSort(s -> s.field(sf -> sf.field(ESOrderDataDTO.COL_UPDATED).order(sortOrder)));
            }
            // 特殊处理-更新时间
            else if (OrderSpecialControlEnum.CREATE.getId().equals(controlId)) {
                queryBuilder.withSort(s -> s.field(sf -> sf.field(ESOrderDataDTO.COL_CREATED).order(sortOrder)));
            } else {
                if (!controlCodeMap.containsKey(controlId)) {
                    throw new BusinessException(BaseEnum.ERROR_PARAMETER, "排序参数错误");
                }
                ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(controlCodeMap.get(controlId));
                String key = ESControlDataValueDTO.COL_DN_V_DV;
                // 日期排序
                if (controlCodeEnum.isDate()) {
                    key = ESControlDataValueDTO.COL_DN_V_DATE;
                }
                // 数字排序
                else if (ControlCodeEnum.INPUT_NUMBER == controlCodeEnum) {
                    key = ESControlDataValueDTO.COL_DN_V_DATA;
                }
                FieldSort.Builder fieldSort = new FieldSort.Builder().field(key)
                        .nested(n -> n.path(ESOrderDataDTO.COL_DN).filter(f -> f.term(t -> t.field(ESControlDataDTO.COL_DN_CI).value(controlId))))
                        .order(sortOrder);
                queryBuilder.withSort(s -> s.field(fieldSort.build()));
            }
        });
    }

    @Builder
    private static class AggField {
        private String controlField;
        private String nestedField;
        private String valueField;
    }

    @Builder
    private static class AggTrees {
        private Agg root;
        private Agg last;
        @Builder.Default
        private List<Agg> aggs = new ArrayList<>();
        private String lastTitle;
        @Builder.Default
        private Boolean preBeingTableControl = false;
    }

    // region Summary
    private static class RelatedSummary extends Summary<String> {

        public RelatedSummary(String title, String value) {
            super(title, value);
        }

        @Override
        public String getTitle() {
            return this.getT();
        }

        @Override
        public Summary<String> copy() {
            return new RelatedSummary(this.getT(), this.getValue());
        }

        @Override
        public Summary<String> copy(String value) {
            return new RelatedSummary(this.getT(), value);
        }
    }
    // endregion
}
